﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using UCS.XNA.CombatBiplanes.Synchronization;

namespace UCS.XNA.CombatBiplanes
{
    public class Airplane : IActionExecutor
    {
        #region Attributes and Properties

        private Rectangle _bounds;
        public Rectangle Bounds
        {
            get { return this._bounds; }
        }

        private Texture2D _texture;
        public Texture2D Texture
        {
            get { return _texture; }
        }

        private Color[,] _colorData;
        public Color[,] ColorData
        {
            get { return this._colorData; }
        }

        private int _id;
        public int ID
        {
            get { return this._id; }
            set { this._id = value; }
        }

        private bool _exploding = false;
        public bool Exploding
        {
            get { return this._exploding; }
        }

        #endregion

        #region Private Members

        private Texture2D _rocketTexture;
        private Texture2D _explosionTexture;
        private Vector2 _position;
        private Vector2 _origin;
        private Vector2 _speed;
        private SpriteBatch _spriteBatch;
        private float _speedFactor = 10f;
        private List<Rocket> _rocketList;
        private int _angle = 0;
        private float _scale = 0.4f;
        private bool _validadeInput;
        private int _realAngle;
        private double _miliseconds;
        private bool _angleChanged = false;
        private int _explodeTime = 0;
        private int _blinkTime = 0;
        private bool _visible = true;

        #endregion

        #region Events

        public event EventHandler<RocketEventArgs> FiredRocket;
        public event EventHandler<EventArgs> Exploded;

        #endregion

        #region Constructors

        public Airplane(int id, Vector2 position, Texture2D texture, SpriteBatch spriteBatch, Texture2D rocketTexture, Texture2D explosionTexture, bool validateInput)
        {
            this._id = id;
            this._position = position;
            this._texture = texture;
            this._spriteBatch = spriteBatch;
            this._rocketTexture = rocketTexture;
            this._rocketList = new List<Rocket>();
            this._explosionTexture = explosionTexture;
            this._validadeInput = validateInput;

            this.InitializeBounds();
            this.InitializeOrigin();
            this.InitializeSpeed();
            this.InitializeColorData();
            this._rocketList.Add(this.CreateRocket(this.GetRealPosition()));
        }

        #endregion

        #region Private Methods

        private void InitializeBounds()
        {
            Vector2 realPosition = this.GetRealPosition();

            this._bounds = new Rectangle(Convert.ToInt32(realPosition.X), Convert.ToInt32(realPosition.Y), Convert.ToInt32(this._texture.Width * this._scale), Convert.ToInt32(this._texture.Height * this._scale));
        }

        private void InitializeOrigin()
        {
            this._origin = new Vector2(this._texture.Width / 2, this._texture.Height / 2); 
        }

        private void InitializeSpeed()
        {
            this._speed = new Vector2((float)Math.Cos(this._angle / (180 / Math.PI)), (float)Math.Sin(this._angle / (180 / Math.PI))) * this._speedFactor;
        }

        private void InitializeColorData()
        {
            Color[] colors1D = new Color[this._texture.Width * this._texture.Height];
            this._texture.GetData(colors1D);

            this._colorData = new Color[this._texture.Width, this._texture.Height];
            for (int x = 0; x < this._texture.Width; x++)
                for (int y = 0; y < this._texture.Height; y++)
                    this._colorData[x, y] = colors1D[x + y * this._texture.Width];
        }

        private Rocket CreateRocket(Vector2 position)
        {
            Rocket rocket = new Rocket(this._rocketTexture, this._explosionTexture, position, this, this._spriteBatch);
            rocket.Exploded += (sender, e) =>
                {
                    this._rocketList.Add(this.CreateRocket(this.GetRealPosition()));
                };

            return rocket;
        }

        private void FireRocket()
        {
            if (this._rocketList.Count > 0)
            {
                this._rocketList.RemoveAt(0);

                GameSynchronizer.Instance.SendActionToEveryone(ActionType.Fire, this, null);
            }
        }

        private void AddAngle(int value)
        {
            int newAngle;
            if (this._realAngle + value > 360)
            {
                newAngle = this._realAngle + value - 360;
            }
            else if (this._realAngle + value < 0)
            {
                newAngle = 360 + this._realAngle + value;
            }
            else
            {
                newAngle = this._realAngle + value;
            }

            this._realAngle = newAngle;
            this._angleChanged = true;
        }

        private Vector2 GetRealPosition()
        {
            return new Vector2(this._position.X + this._origin.X * this._scale, this._position.Y + this._origin.Y * this._scale);
        }

        #endregion

        #region Public Methods

        public void Update(GameTime gameTime, KeyboardState keyboardState)
        {
            if (!this._exploding)
            {
                if (this._validadeInput)
                {
                    if (keyboardState.IsKeyDown(Keys.Left))
                        this.AddAngle(-3);
                    else if (keyboardState.IsKeyDown(Keys.Right))
                        this.AddAngle(3);

                    if (keyboardState.IsKeyDown(Keys.Space))
                        this.FireRocket();
                }

                this.InitializeSpeed();

                if (this._miliseconds >= 100)
                {
                    if (GameSynchronizer.Instance != null)
                        GameSynchronizer.Instance.SendActionToEveryone(ActionType.Move, this, null);
                    if (this._angleChanged)
                    {
                        this._angleChanged = false;
                        GameSynchronizer.Instance.SendActionToEveryone(ActionType.Angle, this, new int[] { this._realAngle });
                    }

                    this._miliseconds = 0;
                }
                else
                    this._miliseconds += gameTime.ElapsedGameTime.TotalMilliseconds;
            }
            else
            {
                this._explodeTime += gameTime.ElapsedGameTime.Milliseconds;
                this._blinkTime += gameTime.ElapsedGameTime.Milliseconds;

                if (this._blinkTime >= 300)
                {
                    this._blinkTime = 0;
                    this._visible = !this._visible;
                }

                if (this._explodeTime >= 2000)
                {
                    if (this.Exploded != null)
                        this.Exploded(this, new EventArgs());

                    this._exploding = false;
                }
            }
        }

        public void Draw(GameTime gameTime)
        {
            if (this._visible)
            {
                this._spriteBatch.Begin();

                this._spriteBatch.Draw(this._texture, this.GetRealPosition(), null, Color.White, (float)(this._angle / (180 / Math.PI)), this._origin, this._scale, SpriteEffects.None, 1);

                this._spriteBatch.End();
            }
        }

        public void Explode()
        {
            GameSynchronizer.Instance.SendActionToEveryone(ActionType.Explode, this, null);
        }

        public Vector2 GetNextPosition()
        {
            return this.GetRealPosition() + this._speed;
        }

        public Matrix GetMatrix()
        {
            Vector2 realPosition = this.GetRealPosition();
            Matrix matrix = Matrix.CreateTranslation(-this._origin.X, -this._origin.Y, 0);
            matrix *= Matrix.CreateRotationZ((float)(this._angle / (180 / Math.PI)));
            matrix *= Matrix.CreateScale(this._scale);
            matrix *= Matrix.CreateTranslation(realPosition.X, realPosition.Y, 0);

            return matrix;
        }

        public void Execute(ActionType actionType, int[] parameters)
        {
            if (actionType == ActionType.Angle)
                this._angle = parameters[0];
            else if (actionType == ActionType.Fire)
            {
                Rocket rocket = this.CreateRocket(this.GetRealPosition());
                rocket.Angle = this._angle;

                if (this.FiredRocket != null)
                    this.FiredRocket(this, new RocketEventArgs(rocket));
            }
            else if (actionType == ActionType.Move)
            {
                this._position += this._speed;
                this.InitializeBounds();
            }
            else if (actionType == ActionType.Explode)
            {
                this._exploding = true;
                this._visible = false;
            }
        }

        #endregion
    }
}
